package avicit.bdp.dds.server.worker.task.java;

import avicit.bdp.common.service.service.FileOperateCommonService;
import avicit.bdp.common.utils.SpringApplicationContext;
import avicit.bdp.common.utils.file.StorageConfThreadLocal;
import avicit.bdp.dds.dispatch.process.Property;
import avicit.bdp.dds.dispatch.process.ResourceInfo;
import avicit.bdp.dds.dispatch.task.AbstractParameters;
import avicit.bdp.dds.dispatch.task.java.JavaParameters;
import avicit.bdp.core.util.json.JSONUtils;
import avicit.bdp.dds.common.Constants;
import avicit.bdp.dds.common.utils.ParameterUtils;
import avicit.bdp.dds.dao.entity.Resource;
import avicit.bdp.dds.server.entity.TaskExecutionContext;
import avicit.bdp.dds.server.utils.ParamUtils;
import avicit.bdp.dds.server.worker.task.AbstractTask;
import avicit.bdp.dds.server.worker.task.CommandExecuteResult;
import avicit.bdp.dds.server.worker.task.ShellCommandExecutor;
import avicit.bdp.dds.service.process.ProcessService;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

/** java task */
public class JavaTask extends AbstractTask {

  /** shell parameters */
  private JavaParameters javaParameters;

  /** shell command executor */
  private final ShellCommandExecutor shellCommandExecutor;

  /** taskExecutionContext */
  private final TaskExecutionContext taskExecutionContext;

  /** process database access */
  protected ProcessService processService;

  private final FileOperateCommonService fileOperateCommonService;

  /** This constant is the Classpath or module path delimiter for different operating systems */
  public static final String PATH_SEPARATOR = System.getProperty("path.separator");

  /** This constant represents the current directory in the Classpath or module path */
  public static final String CLASSPATH_CURRENT_DIR = ".";

  /** The constants used to get the Java installation directory */
  public static final String JAVA_HOME_VAR = "${JAVA_HOME}";

  /**
   * constructor
   *
   * @param taskExecutionContext taskExecutionContext
   * @param logger logger
   */
  public JavaTask(TaskExecutionContext taskExecutionContext, Logger logger) {
    super(taskExecutionContext, logger);

    this.taskExecutionContext = taskExecutionContext;
    this.shellCommandExecutor =
        new ShellCommandExecutor(this::logHandle, taskExecutionContext, logger);
    this.fileOperateCommonService =
        SpringApplicationContext.getBean(FileOperateCommonService.class);
  }

  @Override
  public void init() {
    logger.info("java task params {}", taskExecutionContext.getTaskParams());

    javaParameters =
        JSONUtils.parseObject(taskExecutionContext.getTaskParams(), JavaParameters.class);

    if (javaParameters != null && !javaParameters.checkParameters()) {
      throw new RuntimeException("java任务执行参数错误");
    }

    setMainJarName();

    // replace placeholder
    Map<String, Property> paramsMap =
        ParamUtils.convert(
            ParamUtils.getUserDefParamsMap(taskExecutionContext.getDefinedParams()),
            taskExecutionContext.getDefinedParams(),
            javaParameters.getLocalParametersMap(),
            taskExecutionContext.getCmdTypeIfComplement(),
            taskExecutionContext.getScheduleTime());

    if (paramsMap != null) {
      String args =
          ParameterUtils.convertParameterPlaceholders(
              javaParameters.getMainArgs(), ParamUtils.convert(paramsMap));

      logger.info("main args: {}", args);
      javaParameters.setMainArgs(args);
    }
  }

  protected void setMainJarName() {
    // main jar
    ResourceInfo mainJar = javaParameters.getMainJar();
    if (mainJar != null) {
      String resourceId = mainJar.getId();
      String resourceName;
      if (StringUtils.isBlank(resourceId)) {
        resourceName = mainJar.getRes();
      } else {
        if (resourceId.startsWith(avicit.bdp.core.constant.Constants.LEFT_SQUARE_BRACKET)) {
          resourceId = resourceId.substring(2, resourceId.length() - 2);
        }
        Resource resource = processService.getResourceById(javaParameters.getMainJar().getId());
        if (resource == null) {
          logger.error("resource id: {} not exist", resourceId);
          throw new RuntimeException(String.format("resource id: %s not exist", resourceId));
        }
        resourceName = resource.getFullName().replaceFirst("/", "");
        try {
          fileOperateCommonService.getStorageAndSetThreadLocal(taskExecutionContext.getProjectId());

          fileOperateCommonService.copyServerToLocal(
              resource.getFullName(),
              taskExecutionContext.getExecutePath() + resource.getFullName(),
              false,
              true);

        } catch (IOException e) {
          e.printStackTrace();
        } finally {
          StorageConfThreadLocal.removeThreadLocal();
        }
      }
      mainJar.setRes(resourceName);
      javaParameters.setMainJar(mainJar);
    }
  }

  @Override
  public void handle() throws Exception {
    try {
      // construct process
      CommandExecuteResult commandExecuteResult = shellCommandExecutor.run(buildCommand());
      setExitStatusCode(commandExecuteResult.getExitStatusCode());
      setAppIds(commandExecuteResult.getAppIds());
      setProcessId(commandExecuteResult.getProcessId());
    } catch (Exception e) {
      logger.error("java task error", e);
      setExitStatusCode(Constants.EXIT_CODE_FAILURE);
      throw e;
    }
  }

  /**
   * build command
   *
   * @return command
   * @throws Exception exception
   */
  protected String buildCommand() throws Exception {
    List<String> parameterList = buildParameters(javaParameters);

    String command =
        ParameterUtils.convertParameterPlaceholders(
            String.join(" ", parameterList), taskExecutionContext.getDefinedParams());

    logger.info("java task command: {}", command);

    return command;
  }

  /**
   * build parameters
   *
   * @param javaParameters java parameters
   * @return parameter list
   */
  private List<String> buildParameters(JavaParameters javaParameters) {
    List<String> result = new ArrayList<>();
    result.add(getJavaCommandPath() + "java");

    if (javaParameters.isModulePath()) {
      result.add(buildResourcePath());
    }

    if (javaParameters.getMainJar() != null) {
      result.add("-jar");
      result.add(javaParameters.getMainJar().getRes());
    }

    // main class
    if (StringUtils.isNotEmpty(javaParameters.getMainClass())) {
      result.add(javaParameters.getMainClass());
    }

    // command args
    if (StringUtils.isNotEmpty(javaParameters.getMainArgs())) {
      result.add(javaParameters.getMainArgs());
    }

    return result;
  }

  /**
   * Gets the operating system absolute path to the Java command
   *
   * @return String
   */
  private String getJavaCommandPath() {
    return JAVA_HOME_VAR + File.separator + "bin" + File.separator;
  }

  /**
   * Construct a Classpath or module path based on isModulePath
   *
   * @return String
   */
  private String buildResourcePath() {
    StringBuilder builder = new StringBuilder();
    builder.append("-classpath");
    builder.append(" ").append(CLASSPATH_CURRENT_DIR);

    for (ResourceInfo info : javaParameters.getResourceFilesList()) {
      builder.append(PATH_SEPARATOR);
      builder.append(info.getRes());
    }

    return builder.toString();
  }

  @Override
  public void cancelApplication(boolean cancelApplication) throws Exception {
    // cancel process
    shellCommandExecutor.cancelApplication();
  }

  @Override
  public AbstractParameters getParameters() {
    return javaParameters;
  }

  public static void main(String[] args) {
    System.out.println(System.getProperty("path.separator"));
  }
}
